package com.people.core.log.aspect;

import ai.people.core.common.authenticate.AuthenticatedUserExtraction;
import ai.people.netmon.framework.exception.enums.CommonEnum;
import ai.people.netmon.utils.StringPool;
import ai.people.netmon.utils.StringUtils;
import com.alibaba.fastjson.JSONArray;
import com.people.core.log.properties.ServerLogProperties;
import lombok.RequiredArgsConstructor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Stream;

/**
 * 服务日志通知
 *
 * @author yuanqinglong
 * @date 2022/7/7 11:08
 */
@RequiredArgsConstructor
public class ServerLogAdvice implements MethodInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(ServerLogAdvice.class);


    private final ServerLogProperties serverLogProperties;

    @Value("${spring.application.name:}")
    private String serviceName;


    @Autowired(required = false)
    private List<AuthenticatedUserExtraction> authenticatedUserExtractions;


    /**
     * 调用
     *
     * @param invocation 调用
     * @return {@link Object}
     * @throws Throwable e
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object proceed;
        StringBuilder requestInfo = this.requestInfo(invocation);
        Object result = CommonEnum.SUCCESS.name();
        try {
            proceed = invocation.proceed();
        } catch (Throwable e) {
            result = String.format("\033[%dm%s\033[0m", 31, e.getMessage());
            throw e;
        } finally {
            long duration = System.currentTimeMillis() - startTime;
            requestInfo.append("Execution time: ").append(duration).append("ms").append(StringPool.CRLF);
            requestInfo.append("Execution status: ").append(result).append(StringPool.CRLF);
            String logging = String.format("\033[%dm%s\033[0m", 32, requestInfo);
            if (duration > serverLogProperties.getWarningThreshold()) {
                logger.warn(logging);
            } else {
                logger.info(logging);
            }
        }
        return proceed;
    }


    /**
     * 请求信息
     *
     * @param invocation 调用
     * @return {@link StringBuilder}
     */
    public StringBuilder requestInfo(MethodInvocation invocation) {
        StringBuilder builder = new StringBuilder();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        var username = StringPool.EMPTY;
        if (!CollectionUtils.isEmpty(authenticatedUserExtractions)) {
            for (AuthenticatedUserExtraction authenticatedUserExtraction : authenticatedUserExtractions) {
                if (StringUtils.isNotBlank(authenticatedUserExtraction.authenticatedUsername())) {
                    username = authenticatedUserExtraction.authenticatedUsername();
                    break;
                }
            }
        }
        HttpServletRequest request = attributes.getRequest();
        builder.append(StringPool.CRLF).append("Service name: ").append(serviceName).append(StringPool.CRLF);
        builder.append("Request username: ").append(username).append(StringPool.CRLF);
        builder.append("Request url: ").append(request.getRequestURI()).append(StringPool.CRLF);
        boolean fileArgs = Stream.of(invocation.getMethod().getParameterTypes()).anyMatch(MultipartFile.class::isAssignableFrom);
        if (!fileArgs) {
            builder.append("Request parameters: ").append(JSONArray.toJSONString(invocation.getArguments())).append(StringPool.CRLF);
        }
        builder.append("Request method: ").append(request.getMethod()).append(StringPool.CRLF);
        builder.append("Request target: ").append(invocation.getMethod().getDeclaringClass().getSimpleName()).append(StringPool.HASH).append(invocation.getMethod().getName()).append(StringPool.CRLF);
        return builder;
    }

}
